package com.tx.schedule.factory;

import java.util.ArrayList;
import java.util.List;

import org.quartz.CronExpression;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean;

import com.tx.schedule.model.ScheduleJob;
import com.tx.schedule.util.SpringUtils;

/**
 * 定时任务运行工厂类 【核心类】 实现的是无状态的Job
 * 
 * @author 我慧快乐
 */
@DisallowConcurrentExecution
public class ScheduleJobFactory {

	private static Logger logger = LoggerFactory.getLogger(ScheduleJobFactory.class);

	public static List<String> jobNames = new ArrayList<String>();

	public static String getScheduleJobName(String jobName) {
		if (jobNames.contains(jobName)) { // 如果队列中有id，说明其子任务需要执行
			return jobName;
		}
		return null;
	}

	/**
	 * 更新相应的定时设置 根据job_status做相应的处理
	 * 
	 * @param scheduler
	 * @param job
	 * @param triggerKey
	 * @param trigger
	 * @throws SchedulerException
	 */
	public void updateScheduler(Scheduler scheduler, ScheduleJob job, TriggerKey triggerKey, CronTrigger trigger)
			throws SchedulerException {
		if (job.getJobStatus().equals(ScheduleJob.STATUS_ENABLE)) {// N禁用 Y启用
			if (!trigger.getCronExpression().equalsIgnoreCase(job.getCronExpression())) {
				// 按新的cronExpression表达式重新构建trigger
				trigger = getCronTrigger(scheduler, job);
				// 按新的trigger重新设置job执行
				scheduler.rescheduleJob(triggerKey, trigger);
				logger.info(job.getJobGroup() + "." + job.getJobName() + " 更新完毕,目前cron表达式为:" + job.getCronExpression()
						+ " isSpringBean：" + job.getIsSpringBean() + " concurrent: " + job.getConcurrent());
			}
		} else if (job.getJobStatus().equals(ScheduleJob.STATUS_DISABLE)) {
			scheduler.pauseTrigger(triggerKey);// 停止触发器
			scheduler.unscheduleJob(triggerKey);// 移除触发器
			scheduler.deleteJob(trigger.getJobKey());// 删除任务
			logger.info("[" + job.getJobGroup() + "." + job.getJobName() + "]删除完毕");
		}

	}

	/**
	 * 创建一个定时任务，并做安排
	 * 
	 * @param scheduler
	 * @param job
	 * @throws SchedulerException
	 * @throws Exception
	 */
	public static void createSheduler(Scheduler scheduler, ScheduleJob job) throws Exception {
		// 在工作状态可用时,即job_status = Y,开始创建
		if (job.getJobStatus().equals(ScheduleJob.STATUS_ENABLE)) {
			JobDetail jobDetail = getJobDetail(job);
			// jobName存入到队列 每隔一段时间就会扫描所以需要时检测
			if (!ScheduleJobFactory.jobNames.contains(job.getJobName())) {
				ScheduleJobFactory.jobNames.add(job.getJobName());
			}
			CronTrigger trigger = getCronTrigger(scheduler, job);

			scheduler.scheduleJob(jobDetail, trigger);// 注入到管理类
			logger.info("[" + job.getJobGroup() + "." + job.getJobName() + "]创建完毕");
		}
	}

	private static JobDetail getJobDetail(ScheduleJob job) throws Exception {
		// 新建一个基于Spring的管理Job类
		MethodInvokingJobDetailFactoryBean methodInvJobDetailFB = new MethodInvokingJobDetailFactoryBean();
		// 设置Job组名称
		methodInvJobDetailFB.setGroup(job.getJobGroup());
		// 设置Job名称
		methodInvJobDetailFB.setName(job.getJobName());
		// 定义的任务类为Spring的定义的Bean则调用 getBean方法
		if (job.getIsSpringBean().equals(ScheduleJob.IS_SPRING_YES)) {// 是Spring中定义的Bean
			methodInvJobDetailFB.setTargetObject(SpringUtils.getBean(job.getTargetObject()));
		} else {// 不是
			methodInvJobDetailFB.setTargetObject(Class.forName(job.getClazz()).newInstance());
		}
		// 设置任务方法
		methodInvJobDetailFB.setTargetMethod(job.getTargetMethod());
		// 将管理Job类提交到计划管理类
		methodInvJobDetailFB.afterPropertiesSet();
		/** 并发设置 */
		methodInvJobDetailFB.setConcurrent(job.getConcurrent().equals("Y") ? true : false);

		JobDetail jobDetail = methodInvJobDetailFB.getObject();// 动态
		jobDetail.getJobDataMap().put("scheduleJob", job);
		return jobDetail;
	}

	/**
	 * 创建子任务
	 * 
	 * @param job
	 * @throws Exception
	 */
	public void createChildJob(Scheduler scheduler, ScheduleJob job) throws Exception {
		JobDetail jobDetail = getJobDetail(job);

		// 不按照表达式
		Trigger trigger = TriggerBuilder.newTrigger()
				// 保证键值不一样
				.withIdentity(job.getJobName(), job.getJobGroup())
				// .usingJobData("","")//可以存储数据
				.build();
		/**
		 * 原理： 因为是立即执行,没有用到表达式嘛，所以按照实际的调度创建顺序依次执行
		 */
		scheduler.standby(); // 暂时停止 任务都安排完之后统一启动
								// 解决耗时任务按照顺序部署后执行紊乱的问题
		scheduler.scheduleJob(jobDetail, trigger);// 注入到管理类
		logger.info("子任务[" + job.getJobGroup() + "." + job.getJobName() + "]创建完毕");
	}

	/**
	 * 执行子任务
	 * 
	 * @param mainJob
	 * @throws Exception
	 */

	public void executeChildJobs(Scheduler scheduler, List<ScheduleJob> childJobs) throws Exception {
		for (ScheduleJob scheduleJob : childJobs) {
			createChildJob(scheduler, scheduleJob);
		}
		scheduler.start();// 任务安排完之后启动 解决耗时任务按照顺序部署后执行紊乱的问题
		logger.info("成功执行子任务"+childJobs.size()+"个");
	}

	/**
	 * 获取表达式触发器
	 * 
	 * @author 我慧快乐
	 * @param scheduler
	 * @param scheduleJob
	 * @return
	 * @throws SchedulerException
	 */
	public static CronTrigger getCronTrigger(Scheduler scheduler, ScheduleJob scheduleJob) throws SchedulerException {
		CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression());
		// 按新的表达式构建一个新的trigger
		CronTrigger cronTrigger = TriggerBuilder.newTrigger()
				.withIdentity(scheduleJob.getJobName(), scheduleJob.getJobGroup()).withSchedule(scheduleBuilder)
				.build();
		return cronTrigger;
	}

	/**
	 * 更新定时任务
	 *
	 * @param scheduler
	 * @param scheduleJob
	 * @throws Exception
	 */
	public static void updateScheduleJob(Scheduler scheduler, ScheduleJob scheduleJob) throws Exception {
		try {
			JobDetail jobDetail = getJobDetail(scheduleJob);
			JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
			if (scheduler.checkExists(jobKey)) {
				scheduler.deleteJob(jobKey);
			} else {
			}

			if (scheduleJob.getJobStatus() != null && scheduleJob.getJobStatus().equals(ScheduleJob.STATUS_ENABLE)) {
				CronTrigger trigger = getCronTrigger(scheduler, scheduleJob);
				scheduler.scheduleJob(jobDetail, trigger);
			} else if (scheduleJob.getJobStatus() != null && scheduleJob.getJobStatus().equals(ScheduleJob.STATUS_DISABLE)) {
				// 暂停
				TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
				scheduler.pauseTrigger(triggerKey);
			}
		} catch (SchedulerException e) {
			String message = "更新定时任务失败，错误原因：" + e.getMessage();
			logger.error(message);
			throw new SchedulerException(message, e);
		}
		logger.info("更新任务：[" + scheduleJob.getJobGroup() + "." + scheduleJob.getJobName() + "]成功");
	}

	/**
	 * 运行一次任务
	 * 
	 * @author 我慧快乐
	 * @param scheduler
	 * @param jobName
	 * @param jobGroup
	 * @throws SchedulerException
	 */
	public static void runOnce(Scheduler scheduler, String jobName, String jobGroup) throws SchedulerException {
		JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
		try {
			scheduler.triggerJob(jobKey);
		} catch (SchedulerException e) {
			logger.error("运行一次定时任务失败");
			throw new SchedulerException("运行一次定时任务失败", e);
		}
		logger.info("运行单次任务：[" + jobGroup + "." + jobName + "]成功");
	}

	public static void runOnce(Scheduler scheduler, ScheduleJob scheduleJob) throws SchedulerException {
		runOnce(scheduler, scheduleJob.getJobName(), scheduleJob.getJobGroup());
	}

	/**
	 * 暂停任务
	 * 
	 * @author 我慧快乐
	 * @param scheduler
	 * @param jobName
	 * @param jobGroup
	 */
	public static void pauseJob(Scheduler scheduler, String jobName, String jobGroup) throws SchedulerException {

		JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
		try {
			scheduler.pauseJob(jobKey);
		} catch (SchedulerException e) {
			logger.error("暂停定时任务失败");
			throw new SchedulerException("暂停定时任务失败", e);
		}
		logger.info("暂停任务：[" + jobGroup + "." + jobName + "]成功");
	}

	/**
	 * 暂停任务
	 * 
	 * @author 我慧快乐
	 * @param scheduler
	 * @param scheduleJob
	 * @throws SchedulerException
	 */
	public static void pauseJob(Scheduler scheduler, ScheduleJob scheduleJob) throws SchedulerException {
		pauseJob(scheduler, scheduleJob.getJobName(), scheduleJob.getJobGroup());
	}

	/**
	 * 恢复任务
	 * 
	 * @author 我慧快乐
	 * @param scheduler
	 * @param jobName
	 * @param jobGroup
	 * @throws SchedulerException
	 */
	public static void resumeJob(Scheduler scheduler, ScheduleJob scheduleJob) throws SchedulerException {
		resumeJob(scheduler, scheduleJob.getJobName(), scheduleJob.getJobGroup());
	}

	/**
	 * 恢复任务
	 * 
	 * @author 我慧快乐
	 * @param scheduler
	 * @param jobName
	 * @param jobGroup
	 * @throws SchedulerException
	 */
	public static void resumeJob(Scheduler scheduler, String jobName, String jobGroup) throws SchedulerException {
		JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
		try {
			scheduler.resumeJob(jobKey);
		} catch (SchedulerException e) {
			logger.error("恢复定时任务失败");
			throw new SchedulerException("恢复定时任务失败", e);
		}
		logger.info("恢复任务：[" + jobGroup + "." + jobName + "]成功");
	}

	/**
	 * 删除定时任务
	 *
	 * @param scheduler
	 * @param jobName
	 * @param jobGroup
	 * @throws SchedulerException
	 */
	public static void deleteScheduleJob(Scheduler scheduler, String jobName, String jobGroup)
			throws SchedulerException {
		try {
			scheduler.deleteJob(JobKey.jobKey(jobName, jobGroup));
			jobNames.remove(jobName);
		} catch (SchedulerException e) {
			logger.error("删除定时任务失败");
			throw new SchedulerException("删除定时任务失败", e);
		}
		logger.info("删除任务：[" + jobGroup + "." + jobName + "]成功");
	}

	/**
	 * 删除定时任务
	 *
	 * @param scheduler
	 * @param jobName
	 * @param jobGroup
	 * @throws SchedulerException
	 */
	public static void deleteScheduleJob(Scheduler scheduler, ScheduleJob scheduleJob) throws SchedulerException {
		deleteScheduleJob(scheduler, scheduleJob.getJobName(), scheduleJob.getJobGroup());
	}

	/**
	 * 校验表达式准确性
	 * 
	 * @author 我慧快乐
	 * @param scheduleJob
	 * @return
	 */
	public static Boolean isValidExpression(ScheduleJob scheduleJob) {
		return CronExpression.isValidExpression(scheduleJob.getCronExpression());
	}

	/**
	 * 校验表达式准确性
	 * 
	 * @author 我慧快乐
	 * @param cronExpression
	 * @return
	 */
	public static Boolean isValidExpression(String cronExpression) {
		return CronExpression.isValidExpression(cronExpression);
	}

}